﻿namespace Nintemulator.GBA.CPU.Disassemble
{
    public class Armv4Disassembler
    {
        private static uint Sign(uint value, uint bits)
        {
            if (((value >> (int)(bits - 1U)) & 1U) != 0)
                return value | ((0xFFFFFFFFU >> (int)bits) << (int)bits);

            return value;
        }

        private static uint Ror(uint value, uint shift)
        {
            return (value >> (int)shift) | (value << (int)(32 - shift));
        }

        private static string GetCondition(uint code)
        {
            switch (code >> 28)
            {
            default:
            case 0x0U: return "eq";
            case 0x1U: return "ne";
            case 0x2U: return "cs";
            case 0x3U: return "cc";
            case 0x4U: return "mi";
            case 0x5U: return "pl";
            case 0x6U: return "vs";
            case 0x7U: return "vc";
            case 0x8U: return "hi";
            case 0x9U: return "ls";
            case 0xAU: return "ge";
            case 0xBU: return "lt";
            case 0xCU: return "gt";
            case 0xDU: return "le";
            case 0xEU: return "al";
            case 0xFU: return "nv";
            }
        }

        public static string Disassemble(uint pc, uint code)
        {
            switch ((code >> 16 & 0xFF0U) | (code >> 4 & 0x00FU))
            {
            case 0x121U: // bx{cond} rn
                return string.Format("bx{0}   r{1}", GetCondition(code), (code >> 0) & 15U);

            case 0x109U: return string.Format("swp{0}  r{1},r{2},[r{3}]", GetCondition(code), (code >> 12) & 15U, (code >> 0) & 15U, (code >> 16) & 15U);
            case 0x149U: return string.Format("swp{0}b r{1},r{2},[r{3}]", GetCondition(code), (code >> 12) & 15U, (code >> 0) & 15U, (code >> 16) & 15U);

            case 0x100U: // mrs{cond} rd,cpsr
                return string.Format("mrs{0}  r{1},cpsr", GetCondition(code), (code >> 12) & 15U);
            case 0x120U: // msr{cond} cpsr{_field},rm
                return string.Format("msr{0}  cpsr,r{1}", GetCondition(code), (code >> 0) & 15U);
            case 0x140U: // mrs{cond} rd,spsr
                return string.Format("mrs{0}  r{1},spsr", GetCondition(code), (code >> 12) & 15U);
            case 0x160U: // msr{cond} spsr{_field},rm
                return string.Format("msr{0}  spsr,r{1}", GetCondition(code), (code >> 0) & 15U);

            case 0x320U:case 0x321U:case 0x322U:case 0x323U:case 0x324U:case 0x325U:case 0x326U:case 0x327U:case 0x328U:case 0x329U:case 0x32AU:case 0x32BU:case 0x32CU:case 0x32DU:case 0x32EU:case 0x32FU:
                // MSR{Cond} Cpsr{_field},#Imm = %.... [0011 0010] fsxc 1111 .... [....] ....
                return string.Format("msr{0}  cpsr,#{1}", GetCondition(code), Ror(code & 0xFFU, (code >> 7) & 0x1EU));

            case 0x360U:case 0x361U:case 0x362U:case 0x363U:case 0x364U:case 0x365U:case 0x366U:case 0x367U:case 0x368U:case 0x369U:case 0x36AU:case 0x36BU:case 0x36CU:case 0x36DU:case 0x36EU:case 0x36FU:
                // MSR{Cond} Spsr{_field},#Imm = %.... [0011 0110] fsxc 1111 .... [....] ....
                return string.Format("msr{0}  spsr,#{1}", GetCondition(code), Ror(code & 0xFFU, (code >> 7) & 0x1EU));

            case 0xA00U:case 0xA01U:case 0xA02U:case 0xA03U:case 0xA04U:case 0xA05U:case 0xA06U:case 0xA07U:case 0xA08U:case 0xA09U:case 0xA0AU:case 0xA0BU:case 0xA0CU:case 0xA0DU:case 0xA0EU:case 0xA0FU:
            case 0xA10U:case 0xA11U:case 0xA12U:case 0xA13U:case 0xA14U:case 0xA15U:case 0xA16U:case 0xA17U:case 0xA18U:case 0xA19U:case 0xA1AU:case 0xA1BU:case 0xA1CU:case 0xA1DU:case 0xA1EU:case 0xA1FU:
            case 0xA20U:case 0xA21U:case 0xA22U:case 0xA23U:case 0xA24U:case 0xA25U:case 0xA26U:case 0xA27U:case 0xA28U:case 0xA29U:case 0xA2AU:case 0xA2BU:case 0xA2CU:case 0xA2DU:case 0xA2EU:case 0xA2FU:
            case 0xA30U:case 0xA31U:case 0xA32U:case 0xA33U:case 0xA34U:case 0xA35U:case 0xA36U:case 0xA37U:case 0xA38U:case 0xA39U:case 0xA3AU:case 0xA3BU:case 0xA3CU:case 0xA3DU:case 0xA3EU:case 0xA3FU:
            case 0xA40U:case 0xA41U:case 0xA42U:case 0xA43U:case 0xA44U:case 0xA45U:case 0xA46U:case 0xA47U:case 0xA48U:case 0xA49U:case 0xA4AU:case 0xA4BU:case 0xA4CU:case 0xA4DU:case 0xA4EU:case 0xA4FU:
            case 0xA50U:case 0xA51U:case 0xA52U:case 0xA53U:case 0xA54U:case 0xA55U:case 0xA56U:case 0xA57U:case 0xA58U:case 0xA59U:case 0xA5AU:case 0xA5BU:case 0xA5CU:case 0xA5DU:case 0xA5EU:case 0xA5FU:
            case 0xA60U:case 0xA61U:case 0xA62U:case 0xA63U:case 0xA64U:case 0xA65U:case 0xA66U:case 0xA67U:case 0xA68U:case 0xA69U:case 0xA6AU:case 0xA6BU:case 0xA6CU:case 0xA6DU:case 0xA6EU:case 0xA6FU:
            case 0xA70U:case 0xA71U:case 0xA72U:case 0xA73U:case 0xA74U:case 0xA75U:case 0xA76U:case 0xA77U:case 0xA78U:case 0xA79U:case 0xA7AU:case 0xA7BU:case 0xA7CU:case 0xA7DU:case 0xA7EU:case 0xA7FU:
            case 0xA80U:case 0xA81U:case 0xA82U:case 0xA83U:case 0xA84U:case 0xA85U:case 0xA86U:case 0xA87U:case 0xA88U:case 0xA89U:case 0xA8AU:case 0xA8BU:case 0xA8CU:case 0xA8DU:case 0xA8EU:case 0xA8FU:
            case 0xA90U:case 0xA91U:case 0xA92U:case 0xA93U:case 0xA94U:case 0xA95U:case 0xA96U:case 0xA97U:case 0xA98U:case 0xA99U:case 0xA9AU:case 0xA9BU:case 0xA9CU:case 0xA9DU:case 0xA9EU:case 0xA9FU:
            case 0xAA0U:case 0xAA1U:case 0xAA2U:case 0xAA3U:case 0xAA4U:case 0xAA5U:case 0xAA6U:case 0xAA7U:case 0xAA8U:case 0xAA9U:case 0xAAAU:case 0xAABU:case 0xAACU:case 0xAADU:case 0xAAEU:case 0xAAFU:
            case 0xAB0U:case 0xAB1U:case 0xAB2U:case 0xAB3U:case 0xAB4U:case 0xAB5U:case 0xAB6U:case 0xAB7U:case 0xAB8U:case 0xAB9U:case 0xABAU:case 0xABBU:case 0xABCU:case 0xABDU:case 0xABEU:case 0xABFU:
            case 0xAC0U:case 0xAC1U:case 0xAC2U:case 0xAC3U:case 0xAC4U:case 0xAC5U:case 0xAC6U:case 0xAC7U:case 0xAC8U:case 0xAC9U:case 0xACAU:case 0xACBU:case 0xACCU:case 0xACDU:case 0xACEU:case 0xACFU:
            case 0xAD0U:case 0xAD1U:case 0xAD2U:case 0xAD3U:case 0xAD4U:case 0xAD5U:case 0xAD6U:case 0xAD7U:case 0xAD8U:case 0xAD9U:case 0xADAU:case 0xADBU:case 0xADCU:case 0xADDU:case 0xADEU:case 0xADFU:
            case 0xAE0U:case 0xAE1U:case 0xAE2U:case 0xAE3U:case 0xAE4U:case 0xAE5U:case 0xAE6U:case 0xAE7U:case 0xAE8U:case 0xAE9U:case 0xAEAU:case 0xAEBU:case 0xAECU:case 0xAEDU:case 0xAEEU:case 0xAEFU:
            case 0xAF0U:case 0xAF1U:case 0xAF2U:case 0xAF3U:case 0xAF4U:case 0xAF5U:case 0xAF6U:case 0xAF7U:case 0xAF8U:case 0xAF9U:case 0xAFAU:case 0xAFBU:case 0xAFCU:case 0xAFDU:case 0xAFEU:case 0xAFFU:
                // b{cond} label
                return string.Format("b{0}    ${1:X8}", GetCondition(code), pc + 8U + Sign((code << 2) & 0x3FFFFFCU, 26U));

            case 0xB00U:case 0xB01U:case 0xB02U:case 0xB03U:case 0xB04U:case 0xB05U:case 0xB06U:case 0xB07U:case 0xB08U:case 0xB09U:case 0xB0AU:case 0xB0BU:case 0xB0CU:case 0xB0DU:case 0xB0EU:case 0xB0FU:
            case 0xB10U:case 0xB11U:case 0xB12U:case 0xB13U:case 0xB14U:case 0xB15U:case 0xB16U:case 0xB17U:case 0xB18U:case 0xB19U:case 0xB1AU:case 0xB1BU:case 0xB1CU:case 0xB1DU:case 0xB1EU:case 0xB1FU:
            case 0xB20U:case 0xB21U:case 0xB22U:case 0xB23U:case 0xB24U:case 0xB25U:case 0xB26U:case 0xB27U:case 0xB28U:case 0xB29U:case 0xB2AU:case 0xB2BU:case 0xB2CU:case 0xB2DU:case 0xB2EU:case 0xB2FU:
            case 0xB30U:case 0xB31U:case 0xB32U:case 0xB33U:case 0xB34U:case 0xB35U:case 0xB36U:case 0xB37U:case 0xB38U:case 0xB39U:case 0xB3AU:case 0xB3BU:case 0xB3CU:case 0xB3DU:case 0xB3EU:case 0xB3FU:
            case 0xB40U:case 0xB41U:case 0xB42U:case 0xB43U:case 0xB44U:case 0xB45U:case 0xB46U:case 0xB47U:case 0xB48U:case 0xB49U:case 0xB4AU:case 0xB4BU:case 0xB4CU:case 0xB4DU:case 0xB4EU:case 0xB4FU:
            case 0xB50U:case 0xB51U:case 0xB52U:case 0xB53U:case 0xB54U:case 0xB55U:case 0xB56U:case 0xB57U:case 0xB58U:case 0xB59U:case 0xB5AU:case 0xB5BU:case 0xB5CU:case 0xB5DU:case 0xB5EU:case 0xB5FU:
            case 0xB60U:case 0xB61U:case 0xB62U:case 0xB63U:case 0xB64U:case 0xB65U:case 0xB66U:case 0xB67U:case 0xB68U:case 0xB69U:case 0xB6AU:case 0xB6BU:case 0xB6CU:case 0xB6DU:case 0xB6EU:case 0xB6FU:
            case 0xB70U:case 0xB71U:case 0xB72U:case 0xB73U:case 0xB74U:case 0xB75U:case 0xB76U:case 0xB77U:case 0xB78U:case 0xB79U:case 0xB7AU:case 0xB7BU:case 0xB7CU:case 0xB7DU:case 0xB7EU:case 0xB7FU:
            case 0xB80U:case 0xB81U:case 0xB82U:case 0xB83U:case 0xB84U:case 0xB85U:case 0xB86U:case 0xB87U:case 0xB88U:case 0xB89U:case 0xB8AU:case 0xB8BU:case 0xB8CU:case 0xB8DU:case 0xB8EU:case 0xB8FU:
            case 0xB90U:case 0xB91U:case 0xB92U:case 0xB93U:case 0xB94U:case 0xB95U:case 0xB96U:case 0xB97U:case 0xB98U:case 0xB99U:case 0xB9AU:case 0xB9BU:case 0xB9CU:case 0xB9DU:case 0xB9EU:case 0xB9FU:
            case 0xBA0U:case 0xBA1U:case 0xBA2U:case 0xBA3U:case 0xBA4U:case 0xBA5U:case 0xBA6U:case 0xBA7U:case 0xBA8U:case 0xBA9U:case 0xBAAU:case 0xBABU:case 0xBACU:case 0xBADU:case 0xBAEU:case 0xBAFU:
            case 0xBB0U:case 0xBB1U:case 0xBB2U:case 0xBB3U:case 0xBB4U:case 0xBB5U:case 0xBB6U:case 0xBB7U:case 0xBB8U:case 0xBB9U:case 0xBBAU:case 0xBBBU:case 0xBBCU:case 0xBBDU:case 0xBBEU:case 0xBBFU:
            case 0xBC0U:case 0xBC1U:case 0xBC2U:case 0xBC3U:case 0xBC4U:case 0xBC5U:case 0xBC6U:case 0xBC7U:case 0xBC8U:case 0xBC9U:case 0xBCAU:case 0xBCBU:case 0xBCCU:case 0xBCDU:case 0xBCEU:case 0xBCFU:
            case 0xBD0U:case 0xBD1U:case 0xBD2U:case 0xBD3U:case 0xBD4U:case 0xBD5U:case 0xBD6U:case 0xBD7U:case 0xBD8U:case 0xBD9U:case 0xBDAU:case 0xBDBU:case 0xBDCU:case 0xBDDU:case 0xBDEU:case 0xBDFU:
            case 0xBE0U:case 0xBE1U:case 0xBE2U:case 0xBE3U:case 0xBE4U:case 0xBE5U:case 0xBE6U:case 0xBE7U:case 0xBE8U:case 0xBE9U:case 0xBEAU:case 0xBEBU:case 0xBECU:case 0xBEDU:case 0xBEEU:case 0xBEFU:
            case 0xBF0U:case 0xBF1U:case 0xBF2U:case 0xBF3U:case 0xBF4U:case 0xBF5U:case 0xBF6U:case 0xBF7U:case 0xBF8U:case 0xBF9U:case 0xBFAU:case 0xBFBU:case 0xBFCU:case 0xBFDU:case 0xBFEU:case 0xBFFU:
                // bl{cond} label
                return string.Format("bl{0}   ${1:X8}", GetCondition(code), pc + 8U + Sign((code << 2) & 0x3FFFFFCU, 26U));

            case 0xF00U:case 0xF01U:case 0xF02U:case 0xF03U:case 0xF04U:case 0xF05U:case 0xF06U:case 0xF07U:case 0xF08U:case 0xF09U:case 0xF0AU:case 0xF0BU:case 0xF0CU:case 0xF0DU:case 0xF0EU:case 0xF0FU:
            case 0xF10U:case 0xF11U:case 0xF12U:case 0xF13U:case 0xF14U:case 0xF15U:case 0xF16U:case 0xF17U:case 0xF18U:case 0xF19U:case 0xF1AU:case 0xF1BU:case 0xF1CU:case 0xF1DU:case 0xF1EU:case 0xF1FU:
            case 0xF20U:case 0xF21U:case 0xF22U:case 0xF23U:case 0xF24U:case 0xF25U:case 0xF26U:case 0xF27U:case 0xF28U:case 0xF29U:case 0xF2AU:case 0xF2BU:case 0xF2CU:case 0xF2DU:case 0xF2EU:case 0xF2FU:
            case 0xF30U:case 0xF31U:case 0xF32U:case 0xF33U:case 0xF34U:case 0xF35U:case 0xF36U:case 0xF37U:case 0xF38U:case 0xF39U:case 0xF3AU:case 0xF3BU:case 0xF3CU:case 0xF3DU:case 0xF3EU:case 0xF3FU:
            case 0xF40U:case 0xF41U:case 0xF42U:case 0xF43U:case 0xF44U:case 0xF45U:case 0xF46U:case 0xF47U:case 0xF48U:case 0xF49U:case 0xF4AU:case 0xF4BU:case 0xF4CU:case 0xF4DU:case 0xF4EU:case 0xF4FU:
            case 0xF50U:case 0xF51U:case 0xF52U:case 0xF53U:case 0xF54U:case 0xF55U:case 0xF56U:case 0xF57U:case 0xF58U:case 0xF59U:case 0xF5AU:case 0xF5BU:case 0xF5CU:case 0xF5DU:case 0xF5EU:case 0xF5FU:
            case 0xF60U:case 0xF61U:case 0xF62U:case 0xF63U:case 0xF64U:case 0xF65U:case 0xF66U:case 0xF67U:case 0xF68U:case 0xF69U:case 0xF6AU:case 0xF6BU:case 0xF6CU:case 0xF6DU:case 0xF6EU:case 0xF6FU:
            case 0xF70U:case 0xF71U:case 0xF72U:case 0xF73U:case 0xF74U:case 0xF75U:case 0xF76U:case 0xF77U:case 0xF78U:case 0xF79U:case 0xF7AU:case 0xF7BU:case 0xF7CU:case 0xF7DU:case 0xF7EU:case 0xF7FU:
            case 0xF80U:case 0xF81U:case 0xF82U:case 0xF83U:case 0xF84U:case 0xF85U:case 0xF86U:case 0xF87U:case 0xF88U:case 0xF89U:case 0xF8AU:case 0xF8BU:case 0xF8CU:case 0xF8DU:case 0xF8EU:case 0xF8FU:
            case 0xF90U:case 0xF91U:case 0xF92U:case 0xF93U:case 0xF94U:case 0xF95U:case 0xF96U:case 0xF97U:case 0xF98U:case 0xF99U:case 0xF9AU:case 0xF9BU:case 0xF9CU:case 0xF9DU:case 0xF9EU:case 0xF9FU:
            case 0xFA0U:case 0xFA1U:case 0xFA2U:case 0xFA3U:case 0xFA4U:case 0xFA5U:case 0xFA6U:case 0xFA7U:case 0xFA8U:case 0xFA9U:case 0xFAAU:case 0xFABU:case 0xFACU:case 0xFADU:case 0xFAEU:case 0xFAFU:
            case 0xFB0U:case 0xFB1U:case 0xFB2U:case 0xFB3U:case 0xFB4U:case 0xFB5U:case 0xFB6U:case 0xFB7U:case 0xFB8U:case 0xFB9U:case 0xFBAU:case 0xFBBU:case 0xFBCU:case 0xFBDU:case 0xFBEU:case 0xFBFU:
            case 0xFC0U:case 0xFC1U:case 0xFC2U:case 0xFC3U:case 0xFC4U:case 0xFC5U:case 0xFC6U:case 0xFC7U:case 0xFC8U:case 0xFC9U:case 0xFCAU:case 0xFCBU:case 0xFCCU:case 0xFCDU:case 0xFCEU:case 0xFCFU:
            case 0xFD0U:case 0xFD1U:case 0xFD2U:case 0xFD3U:case 0xFD4U:case 0xFD5U:case 0xFD6U:case 0xFD7U:case 0xFD8U:case 0xFD9U:case 0xFDAU:case 0xFDBU:case 0xFDCU:case 0xFDDU:case 0xFDEU:case 0xFDFU:
            case 0xFE0U:case 0xFE1U:case 0xFE2U:case 0xFE3U:case 0xFE4U:case 0xFE5U:case 0xFE6U:case 0xFE7U:case 0xFE8U:case 0xFE9U:case 0xFEAU:case 0xFEBU:case 0xFECU:case 0xFEDU:case 0xFEEU:case 0xFEFU:
            case 0xFF0U:case 0xFF1U:case 0xFF2U:case 0xFF3U:case 0xFF4U:case 0xFF5U:case 0xFF6U:case 0xFF7U:case 0xFF8U:case 0xFF9U:case 0xFFAU:case 0xFFBU:case 0xFFCU:case 0xFFDU:case 0xFFEU:case 0xFFFU:
                // swi{cond} label
                return string.Format("swi{0}  ${1:X6}", GetCondition(code), code & 0xFFFFFFU);
            }

            return "ERROR";
        }
    }
}